/*
* This class defines a Hidden Markov Model and provides all the methods
* that are necessary to manipulate it
*/
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <vector>
#include <cfloat>
#include <cmath>
#include <cstring>

#include "caction.h"
#include "cdeadline.h"

using namespace std;

namespace ducks
{ 
class HMM
{
    private:

        float **A;
        float **B;
        float *pi;
        float **alpha;
        float *c;
        float **beta;
        float **gamma;
        float ***digamma;
        float acceptState;
        float nextGuessProb;
        int *possibleStates;
        int knownStates;
        int validStates;
        int currT;
        int N;
        int M;

        /*
        * This function must be called when three possible states have been
        * found. It will update the A and B matrixes so that it will be 
        * impossible to reach the fourth state
        */
        void redoMatrix();

        /*
        * Performs an alpha-pass on this model using the observation given
        *
        *\param obs A vector containing the observations
        *\param c Array of the same size as obs where the escaling factors should be saved
        *\param ret Vector where the alphas should be stored. This should have
        * memmory allocated for a TxN matrix
        *\return A matrix of size TxN, where T is the size of obs and N is the
        *        number of states of this HMM. This value is returned in ret
        */
        void alphaPass(vector<int> obs, float *c, float **ret);

    public:
        //Empty constructor
        HMM(){}
        /*
        * This constructor initializes a Hidden Markov Model with random values
        *
        *\param N Number of states in the model
        *\param M Number of observation symbols
        *\return A new Hidden Markov Model where the A, B and pi matrixes are
        *        randomly initialized
        */
        HMM(int N, int M);

        /*
        * Adapts the model contained in this instance to the series of 
        * observations given as parameters
        *
        *\param obs A vector containing all the observations that will estimate
        *           the model. Note that the observations must be mapped from
        *           their original representation to an integer representation
        *           in order to adapt them to this HMM
        *\param maxIters Number of iterations that the model should use in
        *       in order to estimate the matrixes
        */
        void estimate(std::vector<int> obs, int maxIters);

        /*
        * Given the HMM represented by the object contained in this, this 
        * method will determine what is the observation that will most
        * likely be seen in the next time instant.
        * Note: this method must only be called after at least one call to
        * estimate
        *
        *\return The integer value of the most likely observation for the
        * next time instant
        */
        int calculateNextObservation();

        /*
        * Returns the likelyhood of seeing the observation given using the 
        * model contained in this
        */
        float probabilityOf(vector<int> obs);

        /*
        * Class destructor. Eliminates all the dynamically allocated matrices
        */
        ~HMM();

        //Getters
        float **getA(){return this->A;}
        float **getB(){return this->B;}
        float *getPi(){return this->pi;}
        float getNextGuessProb(){return this->nextGuessProb;}
        int getCurrT(){return this->currT;}
        int getN(){return this->N;}
        int getM(){return this->M;}
};
}
